package com.lwq.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lwq.dao.system.MenuMapper;
import com.lwq.dao.system.UserMapper;
import com.lwq.model.TokenModel;
import com.lwq.service.system.MenuService;
import com.lwq.service.system.PermissionService;
import com.lwq.service.system.RoleService;
import com.lwq.exception.ApiException;
import com.lwq.exception.AuthenticationException;
import com.lwq.exception.FieldException;
import com.lwq.model.SysUser;
import com.lwq.util.RedisUtil;
import com.lwq.util.TokenUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Description:
 * Author: LiuWenQing
 * Datetime: 2021/6/10
 */
@Service
public class LoginService {

    @Autowired
    private UserMapper mapper;
    @Autowired
    private RedisUtil redis;
    @Autowired
    private PasswordEncoder pe;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private MenuService menuService;
    @Value("${token.expire}")
    private int tokenExpire;
    @Value("${permission.administratorAccounts}")
    private String adminAccount;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private MenuMapper menuMapper;

    @Transactional(rollbackFor = Exception.class)
    public TokenModel doLogin(SysUser user) {
        SysUser model = this.mapper.selectOne(new QueryWrapper<SysUser>().eq("name", user.getName()));
        if (model == null) {
            throw new AuthenticationException("账号不存在");
        }
        if(model.getStatus().equals(SysUser.DISABLE)){
            throw new AuthenticationException("账号已被禁用");
        }
        if (!this.pe.matches(user.getPassword(), model.getPassword())) {
            throw new AuthenticationException("账号或密码错误!");
        }
        TokenModel tk = null;
        try {
            if (!StringUtils.isEmpty(redis.get(model.getId().toString()))) {
//                String token = redis.get(model.getId().toString());
                redis.del(model.getId().toString());
//                if (!StringUtils.isEmpty(model.getMultiLogin()) &&
//                        model.getMultiLogin().equals(SysUser.BANNEDMULTILOGIN)) {
//                    //不支持多客户端同时登录  --互踢
//                    tk = this.createToken(model);
//                } else {
//                    tk = this.createToken(model);
//                }
                tk = this.createToken(model);
            } else {
                tk = this.createToken(model);
            }
        } catch (Exception e) {
            throw new RuntimeException("创建token异常");
        }
        // 把token放入redis
        redis.put(model.getId().toString(), tk.getAccess(), tokenExpire, TimeUnit.HOURS);
        model.setLastLoginDate(new Date());
        mapper.updateById(model);
        return setPermissionInfo(tk);
    }

    public void  changePass(SysUser user){
        if(!user.getPassword().equals(user.getConfirmPassword())){
            throw new FieldException("两次输入密码不一致");
        }
        SysUser model = mapper.selectById(tokenService.getUserId());
        if(model == null){
            throw new FieldException("账号不存在");
        }
        model.setPassword(pe.encode(user.getPassword()));
        mapper.updateById(model);
    }

    public void register(SysUser user){
        if(!user.getPassword().equals(user.getConfirmPassword())){
            throw new FieldException("两次输入密码不一致");
        }
        if(mapper.selectOne(new QueryWrapper<SysUser>().eq("name", user.getName()))!=null){
            throw new ApiException("账号重复,请重新输入");
        }
        user.setPassword(pe.encode(user.getPassword()));
        mapper.insert(user);
    }

    public void logout(Long userId) {
        redis.del(userId.toString());
        redis.del(userId.toString()+":menu");
        redis.del(userId.toString()+":permission");
    }

    private void cachePermissionInfo(TokenModel tokenModel) {
        redis.setCacheList(tokenModel.getUserId()+RedisUtil.PERMISSIONTAG, tokenModel.getPermissions(),tokenExpire,TimeUnit.HOURS);
        redis.setCacheList(tokenModel.getUserId()+RedisUtil.MENUTAG, tokenModel.getMenus(),tokenExpire,TimeUnit.HOURS);
    }

    private TokenModel setPermissionInfo(TokenModel tokenModel) {
        // 判断是否是管理员账号
        Boolean flag = Arrays.asList(adminAccount.split(",")).contains(tokenModel.getName());
        tokenModel.setRoles(flag ? null : roleService.getRolesByUser(tokenModel.getUserId()));
        tokenModel.setPermissions(flag ? permissionService.getAllPermissions(null): (tokenModel.getRoles().isEmpty() ? null :
                permissionService.getPermissionsByUser(tokenModel.getUserId(),
                        tokenModel.getRoles().stream().map(e -> e.getId()).collect(Collectors.toList()))));
        tokenModel.setMenus(flag ? menuMapper.selectList(null): (tokenModel.getRoles().isEmpty() ? null :
                menuService.getMenuByUser(tokenModel.getUserId(),
                        tokenModel.getRoles().stream().map(e -> e.getId()).collect(Collectors.toList()))));
        if(!flag){
            cachePermissionInfo(tokenModel);
        }
        return tokenModel;
    }

    private TokenModel createToken(SysUser user) throws Exception {
        TokenModel tk = new TokenModel();
        tk.setAccess(TokenUtil.createToken(user, tokenExpire));
        tk.setUserId(user.getId());
        tk.setName(user.getName());
        tk.setUserName(user.getUserName());
        return tk;
    }

    private TokenModel createToken(SysUser user, String token) {
        TokenModel tk = new TokenModel();
        tk.setAccess(token);
        tk.setUserId(user.getId());
        tk.setName(user.getName());
        tk.setUserName(user.getUserName());
        return tk;
    }
}
